Intro to Shiny


Overview

??We often put an overview slide that links to the various parts of the course content to make it easy to navigate i.e. this one for intro to r??

Set Up


Materials

??We try to denote each major section of content with these title slides (see above for set up). The hierarchy will help with the different versions of the teaching content i.e. slides and single page.??

?? Materials just gives the links to download the package Just ensure links are up to date??

All prerequisites, links to material and slides for this course can be found on github.

Or can be downloaded as a zip archive from here.

Course materials

?? this slide just shows where thye can get the course content from the download. You do not need to populate these directories. It will be made during compilation??

Once the zip file in unarchived. All presentations as HTML slides and pages, their R code and HTML practical sheets will be available in the directories underneath.

  • r_course/presentations/slides/ Presentations as an HTML slide show.
  • r_course/presentations/singlepage/ Presentations as an HTML single page.
  • r_course/presentations/r_code/ R code in presentations.
  • r_course/exercises/ Practicals as HTML pages.
  • r_course/answers/ Practicals with answers as HTML pages and R code solutions.

Set the Working directory

Before running any of the code in the practicals or slides we need to set the working directory to the folder we unarchived.

You may navigate to the unarchived RU_Course_help folder in the Rstudio menu.

Session -> Set Working Directory -> Choose Directory

or in the console.

setwd("/PathToMyDownload/RU_Course_template/r_course")
# e.g. setwd("~/Downloads/Intro_To_R_1Day/r_course")

What is Shiny? Why do we use it?


Basic componenets of Shiny app

A shiny app consists of the user interface (UI) and the server function * the UI object defines what will be seen on the page * the server function contains code that combines R code with inputs (eg info from user, databases, or files) and provides instructions for generating outputs * the shinyApp function builds the shiny app from the the UI object and server function

Neth, H. (2025) Introduction to Data Science (https://bookdown.org/hneth/i2ds/)

Shiny - how to get started

A Shiny app is generally contained within one R script to define the UI object and the server function.

library(shiny)
library(bslib)

ui = page_fluid(
  textOutput(outputId = "app_info")
)

server = function(input, output) {
  output$app_info = renderText("Our first app!")
}

shinyApp(ui = ui, server = server)

Shiny - how to get started

When you run the shinyApp function, your RStudio console will be busy and you’ll see a message with a URL. This is the port within your computer that the app is running.

A window should pop up automatically showing the app. You can also copy and paste this address into a browser to open an instance of the app.

To close the app and free up the console, you can: * click on the console to hit the ESC button or Ctrl+C * click on the Stop button on the top right corner of the console

Shiny - how to get started

Pretty much any Shiny app will start with the code below. Just paste this code into a fresh R script to get started.

TIP: Open a new R script, type shinyapp, then hit Shift+Tab, and the template will appear.

Boiler plate Shiny app code:

library(shiny)

ui <- page_fluid()

server <- function(input, output, session) {
  
}

shinyApp(ui, server)

Shiny - how to get started

Shiny is well integrated into R Studio and there are a few additional features that make running apps easier.

If you open a blank R script and add the shiny template code (see previous slide), once you save the file you should see a button in the upper right corner of the script that says Run App.

This will automatically launch the app.

Shiny - how to get started

How that app is launched (e.g. in browser or a window within RStudio) can be changed by the dropdown that is part of the Run App button

Shiny - how to get started

You can also start a new app by opening a new file in R Studio and selecting the Shiny Web App option.

The Application name you enter will be the name of a newly created directory that contains the app.R file and your Shiny app.

]

]

Shiny resources for inputs and outputs

The Shiny cheatsheet is a great resource demonstrating built-in input and output options

ADD IMAGE OF INPUTS FREOM CHEATSHEET!

or go through inputs like mastering shiny book

or go through the posit widget gallery

Build a basic app

Talk about outputs and how outputs are paired with render functions

ui = page_fluid(
  textOutput(outputId = "app_info")
)

print(as.character(ui))
## [1] "<div class=\"container-fluid\">\n  <div id=\"app_info\" class=\"shiny-text-output\"></div>\n</div>"

Build a basic app

server function and launch app

server = function(input, output) {
  output$app_info = renderText("Our first app!")
}
shinyApp(ui = ui, server = server)

Adding global variables to app

Global variables can be assigned in the script outside of the UI object and server function. This code is run once upon initiating the app and global variables and settings are available within the app.

# read in table
#de_table <- read.csv("shPTBP1_vs_control_DEG.csv")
de_table <- read.csv("data/shP53_vs_control_DEG.csv")
de_table$negLog10_pval <- -log10(de_table$pvalue)

# view table (would not be part of shiny script)
head(de_table, 3)
##                ID Symbol baseMean log2FoldChange      lfcSE      stat pvalue
## 1 ENSG00000002745  WNT16 1531.822      -3.766480 0.08955896 -42.05587      0
## 2 ENSG00000026025    VIM 1578.109       5.556894 0.12318261  45.11103      0
## 3 ENSG00000104419  NDRG1 6651.576      -3.614629 0.07273378 -49.69671      0
##   padj negLog10_pval
## 1    0           Inf
## 2    0           Inf
## 3    0           Inf

App with datatable from DT package

UI object - add the table using the dataTableOutput function from the DT package

library(DT)

ui_simple = page_fluid(
  
  textOutput(outputId = "app_info"),
  
  DT::DTOutput(outputId = "de_data")
)

App with datatable from DT package

Make the server function and launch app

Here we use the renderDataTable function, which will put the table in the UI where we specified with the dataTableOutput function

server_simple = function(input, output) {
  output$app_info = renderText("This is an app showing differential gene expression data")
  
  output$de_data = renderDataTable({
    datatable(de_table) 
  })
}

App with datatable from DT package

shinyApp(ui = ui_simple, server = server_simple)

Customize datatable

The DT package allows for a lot of customization of the html datatable. This link shows some of the capabilities.

Below we will add custom filters on top of each column and round the values to improve the appearance.

server_simple2 = function(input, output) {
  output$app_info = renderText("This is an app showing differential gene expression data")
  
  output$de_data = renderDataTable({
    datatable(de_table,
              filter = 'top') %>%
      formatRound(columns = c("baseMean", "log2FoldChange", "lfcSE", "stat"), digits = 3) %>%
      formatSignif(columns = c("pvalue", "padj"), digits = 3)
  })
}

Customize datatable

shinyApp(ui = ui_simple, server = server_simple2)

Other options for displaying tables

  • rhandsontable - based on javascript handsontable
    • an excel like table that is easily editable by the user
  • reactable - based on java script React Table
    • similar to DT. depends on use case.

Add plots to app

Add an MA plot and a Volcano plot to the page. First add the outputs to the UI object

ui_data = page_fluid(
  textOutput(outputId = "app_info"),
  
  dataTableOutput(outputId = "de_data"),
  
  plotOutput("ma_plot"),
  
  plotOutput("volcano_plot")
)

Add plots to app

Then make the server function containing the render functions that tell shiny how to make the outputs from the IU object.

server_data = function(input, output) {
  output$app_info = renderText("This is an app showing differential gene expression data")
  
  output$de_data = renderDataTable({
    datatable(de_table,filter = 'top') %>%
      formatRound(columns = c("baseMean", "log2FoldChange", "lfcSE", "stat"), digits = 3) %>%
      formatSignif(columns = c("pvalue", "padj"), digits = 3)
  })
  
  output$ma_plot = renderPlot({
    ggplot(de_table, aes(x = baseMean, y = log2FoldChange)) +
      geom_point() +
      scale_x_log10() +
      xlab("baseMean (log scale)") +
      theme_bw() +
      ggtitle("MA plot")
  })
  
  output$volcano_plot = renderPlot({
    ggplot(de_table, aes(x = log2FoldChange, y = negLog10_pval)) +
      geom_point() +
      theme_bw() +
      ggtitle("Volcano plot")
  })
}

Add plots to app

shinyApp(ui = ui_data, server = server_data)

App layouts


Tidy up app appearance with cards

bslib has many functions that allow customizing the formatting of the page. Here we add ‘cards’, which are boxes that allow grouping of UI components

ui_fillable <- page_fillable(

  card(card_header("Table of DE results"),
       dataTableOutput(outputId = "de_data")),
  card(card_header("MA plot"),
       plotOutput("ma_plot")),
  card(card_header("Volcano plot"),
       plotOutput("volcano_plot"))
)

Tidy up app appearance with cards

#same server function as previous
shinyApp(ui = ui_fillable, server = server_data)

Collapsable boxes with accordians

We can add the ability to hide certain boxes with accordians from the bslib package

ui_accordian <- page_fillable(
  accordion(
    accordion_panel("Table of DE results",
                    dataTableOutput(outputId = "de_data")),
    
    accordion_panel("MA plot",
                    plotOutput("ma_plot")),
    
    accordion_panel("Volcano plot",
                    plotOutput("volcano_plot"))
  )
)

Collapsable boxes with accordians

#same server function as previous
shinyApp(ui = ui_accordian, server = server_data)

Collapsable boxes with accordians

Control over which panels can be opened initially can be set in the accordian function

ui_accordian <- page_fillable(
  accordion(
    accordion_panel("Table of DE results",
                    dataTableOutput(outputId = "de_data")),
    
    accordion_panel("MA plot",
                    plotOutput("ma_plot")),
    
    accordion_panel("Volcano plot",
                    plotOutput("volcano_plot")),
    
    open = TRUE # will open all panels
  )
)

Collapsable boxes with accordians

#same server function as previous
shinyApp(ui = ui_accordian, server = server_data)

Customize layout with columns

The layout_columns function from bslib creates columns on the page

ui_column <- page_fillable(
  layout_columns(
    col_widths = c(4,4,4),
    card(card_header("Table of DE results", dataTableOutput(outputId = "de_data"))),
    
    card(card_header("MA plot",plotOutput("ma_plot"))),
    
    card(card_header("Volcano plot",plotOutput("volcano_plot")))
    )
  )

Customize layout with columns

#same server function as previous
shinyApp(ui = ui_column, server = server_data)

Multiple rows

The ‘col_widths’ argument of the layout_columns function will control with width of the column. The bootstrap grid system is made up of 12 columns. A numeric vector is provided with widths for each card. Once the elements combined width goes above 12, then the elements are wrapped to the next row.

ui_twoRow <- page_fillable(
  layout_columns(
    card(card_header("Table of DE results", dataTableOutput(outputId = "de_data"))),
    
    card(card_header("MA plot",plotOutput("ma_plot"))),
    
    card(card_header("Volcano plot",plotOutput("volcano_plot"))),
    
    col_widths = c(12,6,6)
  ),
)

Multiple rows

#same server function as previous
shinyApp(ui = ui_twoRow, server = server_data)

Page layout options

The previous app demonstrates how page_fillable will automatically fill the whole page (both vertically and horizontally). This can be controlled with the ‘col_widths’ and ‘row_heights’ arguments of the layout_columns function

bslib has two other options for the base of the page layout, page_fixed and page_fluid. The page_fixed function will use a fixed width that will vary based on the settings of the device, and the page_fluid function will extend to fill the whole width of the webpage.

Both page_fixed and page_fluid will choose a sensible default height that can be set with the ‘row_heights’ argument of the layout_columns function.

Fixed page

ui_fixed <- page_fixed(
  layout_columns(
    card(card_header("Table of DE results", dataTableOutput(outputId = "de_data"))),
    
    card(card_header("MA plot",plotOutput("ma_plot"))),
    
    card(card_header("Volcano plot",plotOutput("volcano_plot"))),
    
    col_widths = c(12,6,6)
  )
)

Fixed page

#same server function as previous
shinyApp(ui = ui_fixed, server = server_data)

Fluid page

ui_fluid <- page_fluid(
  layout_columns(
    card(card_header("Table of DE results", dataTableOutput(outputId = "de_data"))),
    
    card(card_header("MA plot",plotOutput("ma_plot"))),
    
    card(card_header("Volcano plot",plotOutput("volcano_plot"))),
    
    col_widths = c(12,6,6)
  )
)

Fluid page

#same server function as previous
shinyApp(ui = ui_fluid, server = server_data)

Nested rows within a column

More complicated layouts can be achieved by nesting layout_columns functions. Here we add a tall card as a new row below the table and then nest the plots within this row next to the card.

ui_nested <- page_fluid(
  layout_columns(
    col_widths = 12,
    card(card_header("Table of DE results", dataTableOutput(outputId = "de_data")))),
  
  layout_columns(
    col_widths = 6,
    card(card_header("This is a tall box")),
    
    layout_columns(
      col_widths = c(12,12),
      card(card_header("MA plot",plotOutput("ma_plot"))),
      
      card(card_header("Volcano plot",plotOutput("volcano_plot")))
    )
  ),
)

Nested rows within a column

#same server function as previous
shinyApp(ui = ui_nested, server = server_data)

Add a sidebar to the page

Sidebars are retractable and will remain static as you scroll up and down the page. They are often nice for user inputs or information that you always want the user to see. The width can be set within the function.

ui_sidebar <- page_sidebar(
  title = "RNAseq tools",
  
  sidebar = sidebar(
    "This is a sidebar",
    width = 300,
  ),
  
  layout_columns(
    card(card_header("Table of DE results"), dataTableOutput(outputId = "de_data")),
    card(card_header("MA plot"),plotOutput("ma_plot")),
    card(card_header("Volcano plot"),plotOutput("volcano_plot")),
    col_widths = c(12,6,6), row_heights = c("750px", "500px")
  )
)

Add a sidebar to the page

NOTE SHOW THAT YOU CAN ALSO ADD A SIDEBAR TO A CARD???

#same server function as previous
shinyApp(ui = ui_sidebar, server = server_data)

Use a navbar layout for multi-page app

explain how you can have a side bar in one page or all pages!

ui_navbar <- page_navbar(
  title = "RNAseq tools",
  nav_panel(
    title = "DE Analysis",
    layout_sidebar(
      sidebar = sidebar(
        "This is a sidebar",
        width = 300,
      ),
      
      layout_columns(
        card(card_header("Table of DE results"), dataTableOutput(outputId = "de_data")),
        card(card_header("MA plot"),plotOutput("ma_plot")),
        card(card_header("Volcano plot"),plotOutput("volcano_plot")),
        col_widths = c(12,6,6), row_heights = c("750px", "500px")
      )
    )
  ),
  nav_panel(
    title = "Next steps",
    "The next step in our analysis will be..."
  ),
  nav_spacer(),
  nav_menu(
    title = "Links",
    align = "right",
    nav_item(
      tags$a(
        shiny::icon("chart-simple"), "RU BRC - Learn more!",
        href = "https://rockefelleruniversity.github.io/",
        target = "_blank"
      )
    )
  )
)

Use a navbar layout for multi-page app

#same server function as previous
shinyApp(ui = ui_navbar, server = server_data)

Themes


Themes

It’s easy to modify the look of the app using the bslib package. The page_sidebar function (and other payout functions) has a ‘theme’ argument that takes a bs_theme object.

bslib has builtin themes that can be easily used. The themes can be previewed (here)[https://bootswatch.com/], and the string to use in the ‘bootswatch argument’ of the bs_theme function can be picked from the vector returned by bootswatch_themes()

bootswatch_themes()
##  [1] "cerulean"  "cosmo"     "cyborg"    "darkly"    "flatly"    "journal"  
##  [7] "litera"    "lumen"     "lux"       "materia"   "minty"     "morph"    
## [13] "pulse"     "quartz"    "sandstone" "simplex"   "sketchy"   "slate"    
## [19] "solar"     "spacelab"  "superhero" "united"    "vapor"     "yeti"     
## [25] "zephyr"

Themes - Cerulean

ui_cerulean <- page_navbar(
  title = "RNAseq tools",
  theme = bs_theme(version = 5, bootswatch = "cerulean"),
  nav_panel(
    title = "DE Analysis",
    layout_sidebar(
      sidebar = sidebar(
        "This is a sidebar",
        width = 300,
      ),
      
      layout_columns(
        card(card_header("Table of DE results"), dataTableOutput(outputId = "de_data")),
        card(card_header("MA plot"),plotOutput("ma_plot")),
        card(card_header("Volcano plot"),plotOutput("volcano_plot")),
        col_widths = c(12,6,6), row_heights = c("750px", "500px")
      )
    )
  ),
  nav_panel(
    title = "Next steps",
    "The next step in our analysis will be..."
  ),
  nav_spacer(),
  nav_menu(
    title = "Links",
    align = "right",
    nav_item(
      tags$a(
        shiny::icon("chart-simple"), "RU BRC - Learn more!",
        href = "https://rockefelleruniversity.github.io/",
        target = "_blank"
      )
    )
  )
)

Themes - Cerulean

#same server function as previous
shinyApp(ui = ui_cerulean, server = server_data)

Themes - Darkly

ui_darkly <- page_navbar(
  title = "RNAseq tools",
  theme = bs_theme(version = 5, bootswatch = "darkly"),
  nav_panel(
    title = "DE Analysis",
    layout_sidebar(
      sidebar = sidebar(
        "This is a sidebar",
        width = 300,
      ),
      
      layout_columns(
        card(card_header("Table of DE results"), dataTableOutput(outputId = "de_data")),
        card(card_header("MA plot"),plotOutput("ma_plot")),
        card(card_header("Volcano plot"),plotOutput("volcano_plot")),
        col_widths = c(12,6,6), row_heights = c("750px", "500px")
      )
    )
  ),
  nav_panel(
    title = "Next steps",
    "The next step in our analysis will be..."
  ),
  nav_spacer(),
  nav_menu(
    title = "Links",
    align = "right",
    nav_item(
      tags$a(
        shiny::icon("chart-simple"), "RU BRC - Learn more!",
        href = "https://rockefelleruniversity.github.io/",
        target = "_blank"
      )
    )
  )
)

Themes - Darkly

#same server function as previous
shinyApp(ui = ui_darkly, server = server_data)

Custom themes

A big benefit of the bs_theme function is the ability to highly customize the app theme. This can be done with the arguments to the function, or with additional CSS. We use custom CSS to modify the header of the cards throughout the app and manually set the main color options.

custom_css <- "
  .card-header {
    background-color: #d3dff1;
    border-bottom: 2px solid #273449;
  }
"

# Create theme with custom CSS
custom_theme <- bs_theme(
  version = 5,
  bg = "white",
  fg = "#273449",
  primary = "#5886b2",
  secondary = "#95a5a6",
  success = "#18bc9c",
  info = "#3498db",
  warning = "#f39c12",
  danger = "#e74c3c",
  preset = "bootstrap",
  "navbar-bg" = "#5886b2"
) |> bs_add_rules(custom_css)

Custom themes

This custom theme object can then be used in the ‘theme’ argument of page_sidebar.

ui_custom <- page_navbar(
  title = "RNAseq tools",
  theme = custom_theme,
  nav_panel(
    title = "DE Analysis",
    layout_sidebar(
      sidebar = sidebar(
        "This is a sidebar",
        width = 300,
      ),
      
      layout_columns(
        card(card_header("Table of DE results"), dataTableOutput(outputId = "de_data")),
        card(card_header("MA plot"),plotOutput("ma_plot")),
        card(card_header("Volcano plot"),plotOutput("volcano_plot")),
        col_widths = c(12,6,6), row_heights = c("750px", "500px")
      )
    )
  ),
  nav_panel(
    title = "Next steps",
    "The next step in our analysis will be..."
  ),
  nav_spacer(),
  nav_menu(
    title = "Links",
    align = "right",
    nav_item(
      tags$a(
        shiny::icon("chart-simple"), "RU BRC - Learn more!",
        href = "https://rockefelleruniversity.github.io/",
        target = "_blank"
      )
    )
  )
)

Themes

#same server function as previous
shinyApp(ui = ui_custom, server = server_data)

Exercises

Exercise on Reproducibility in R can be found here

Contact

Any suggestions, comments, edits or questions (about content or the slides themselves) please reach out to our GitHub and raise an issue.

– ## Exercises The following few slides show you how to structure exercise slides.

We often have several exercise slides per session. So you can just copy and paste and change the directory to the appropriate name. All 3 file types are made from you single exercise Rmd.

Time for an exercise!

??? exercise description here?? here

Answers to exercise

Answers can be found here

R code for solutions can be found here